﻿#include "pch.h"
#include "sbhs.h"
#include "sbctn.h"
#include "kh_compiler.h"
#include "kh_code.h"
#include "extend/sbmnehs.h"

//////////////////////////////////////////////////////////////////////////
// 문자열로

// 토큰 타입 -> 문자열
const kchar* kh_tos_token_type(kint v)
{
	static const kchar* s_v[] =
	{
		"Unknown",
		"Identifier",
		"Constant",
		"Argument",
		"Delclaration",
		"Statement",
		"Expression",
		"Operator",
		"Keyword",
		"Reserved",
	};
	return ((ksize_t)v <= K_COUNTOF(s_v)) ? s_v[v] : s_v[0];
}

// 토큰 하는일 -> 문자열
const kchar* kh_tos_token_role(kint v)
{
	static const kchar* s_v[] =
	{
		"Unknown",

		// 식별자 IDENTIFIER
		"Unknown",
		"Public",
		"Self",
		"Local",

		// 상수 CONST
		"Null",
		"Bool",
		"Int",
		"Long",
		"Float",
		"Double",
		"String",
		"WideString",

		// 인수 ARGUMENT
		"...",
		"Argument",
		"Decoration",

		// 선언 DECL
		"Public",
		"Self",
		"Local",

		// 문법 STMT
		"Compound",
		"Return",
		"Function",
		"If",
		"While",
		"Do",
		"For",
		"ForEach",
		"ForEach",
		"Switch",
		"Case",
		"Default",
		"Continue",
		"Break",
		"Label",
		"Jump",
		"Line",
		"BreakPoint",
		"Suspend",

		// 수식 EXPR
		"Self",
		"Table",
		"Array",
		"Function",
		"Call",

		// 연산 OP
		"Assign",
		"Field",
		"Index",
		"Unary Plus",
		"Unary Minus",
		"Unary Not",
		"Unary Sig",
		"Unary Increment",
		"Unary Decrement",
		"Mul",
		"Div",
		"Mod",
		"Add",
		"Sub",
		"ShiftLeft",
		"ShiftRight",
		"LessThan",
		"LessEqual",
		"Equal",
		"NotEqual",
		"And",
		"Xor",
		"Or",
		"Logical And",
		"Logical Or",
		"Increment",
		"Decrement",
		"Index",
		"Dot",
		"SizeOf",
		"TypeName",
		"New",

		// 예약어
		"BkIsGood",

		/**/
		"~~",
	};
	return ((ksize_t)v <= K_COUNTOF(s_v)) ? s_v[v] : s_v[0];
}

// 스코프를 문자열로
const kchar* kh_tos_scope(kint v)
{
	switch (v)
	{
		case /*KHSCP_PUBLIC*/ -2:
			return "public";

		case /*KHSCP_SELF*/ -3:
			return "self";

		default:
			if (v >=/*KHSCP_LOCAL*/0)
				return "local";
	}

	return "unknown";
}

// 이진 코드 의사 명령 -> 문자열
const kchar* kh_tos_mnemonic(kint v)
{
	static const kchar* s_s[] =
	{
		"nop",

		"pos",          // unary plus
		"neg",          // unary minus
		"sig",
		"not",
		"inc",
		"dec",

		"mul",
		"div",
		"mod",
		"add",
		"sub",
		"shl",
		"shr",
		"blt",
		"ble",
		"beq",
		"bne",
		"and",
		"xor",
		"or",

		"stelem",
		"ldelem",
		"stfld",
		"ldfld",

		"br",
		"brz",
		"brne",
		"brnz",
		"bre",

		"stpub",
		"stmem",
		"stloc",

		"ldpub",
		"ldmem",
		"ldloc",

		"ldc.i.0",
		"ldc.i.0",
		"ldc.i.1",

		"ldc.i.0",
		"ldc.i.1",
		"ldc.i.2",
		"ldc.i.3",
		"ldc.i.4",
		"ldc.i.5",
		"ldc.i.6",
		"ldc.i.7",
		"ldc.i.8",

		"ldc.i4",
		"ldc.i8",

		"ldc.r4",
		"ldc.r8",

		"ldsym",

		"ldself",
		"ldstr",
		"ldwcs",
		"ldfn",

		"pop",
		"dup",

		"end",
		"ret",
		"call",

		"prepeach",
		"dispeach",
		"loopeach",
		"loopeach",

		"select",
		"prepselect",
		"dispselect",
		"brselect",

		"newobj",
		"newtable",
		"newarray",
		"newclass",
		"newstruct",
		"newenum",
		"newdef",

		"alias",
		"tblelem",

		"text",
		"sizeof",
		"typename",
		"line",
		"breakpoint",
		"suspend",

		"t_ifelse",
		"t_and",
		"t_or",

		"bkisgood",

		"a",
	};

	return ((ksize_t)v < K_COUNTOF(s_s)) ? s_s[v] : s_s[0];
}


//////////////////////////////////////////////////////////////////////////
// 코드 데이터

// 초기화
void kh_cd_init(khCodeData* cd)
{
	cd->cs = 0;
	cd->ms = 0;

	cd->loc = 0;
	cd->capa = 1 << 6;

	cd->code = k_new(1 << 6, kuint);
	cd->line = k_new(1 << 6, kushort);
}

// 제거
void kh_cd_disp(khCodeData* cd)
{
	k_delete(cd->code);
	k_delete(cd->line);
}

// 리셋
void kh_cd_reset(khCodeData* cd)
{
	cd->cs = 0;
	cd->ms = 0;
	cd->loc = 0;
}

// 확장
void kh_cd_expand(khCodeData* cd, kint cnt)
{
	kint newsize = cd->loc + cnt;
	if (newsize > cd->capa)
	{
		kint n = cd->capa;
		while (n < newsize) n <<= 1;
		cd->code = k_renew(cd->code, n, kuint);
		cd->line = k_renew(cd->line, n, kushort);
		cd->capa = n;
	}
}

// 스킵
kint kh_cd_skip(khCodeData* cd, kuint line)
{
	kint n = cd->loc;
	kh_cd_expand(cd, 1);
	cd->code[cd->loc] = 0;
	cd->line[cd->loc] = (kushort)line;
	cd->loc++;
	return n;
}

// 스킵2
kint kh_cd_skip_2(khCodeData* cd, kuint line)
{
	kint n = cd->loc;
	kh_cd_expand(cd, 2);
	*(klong*)&cd->code[cd->loc] = 0;
	cd->line[cd->loc] = cd->line[cd->loc + 1] = (kushort)line;
	cd->loc += 2;
	return n;
}

// 스킵스
kint kh_cd_skip_nth(khCodeData* cd, kuint line, kint cnt)
{
	if (cnt == 0)
		return cd->loc;
	else
	{
		kint i, n;
		n = cd->loc;
		kh_cd_expand(cd, cnt);
		for (i = 0; i < cnt; i++)
		{
			cd->code[n + i] = 0;
			cd->line[n + i] = (kushort)line;
		}
		cd->loc += cnt;
		return n;
	}
}

// 하나짜리
void kh_cd_emit(khCodeData* cd, kuint line, kuint inst)
{
	kh_cd_expand(cd, 1);
	cd->code[cd->loc] = inst;
	cd->line[cd->loc] = (kushort)line;
	cd->loc++;
}

// 오퍼랜드
void kh_cd_emit_op(khCodeData* cd, kuint line, kuint inst, kint op)
{
	kh_cd_expand(cd, 2);
	cd->code[cd->loc] = inst;
	cd->code[cd->loc + 1] = *(kuint*)&op;
	cd->line[cd->loc] = cd->line[cd->loc + 1] = (kushort)line;
	cd->loc += 2;
}

// 주소
void kh_cd_emit_addr(khCodeData* cd, kuint line, kuint inst, kint addr)
{
	kh_cd_expand(cd, 2);
	cd->code[cd->loc] = inst;
	cd->code[cd->loc + 1] = addr;
	cd->line[cd->loc] = cd->line[cd->loc + 1] = (kushort)line;
	cd->loc += 2;
}

// 심볼
void kh_cd_emit_sym(khCodeData* cd, kuint line, kuint inst, kuint sym)
{
	kh_cd_expand(cd, 2);
	cd->code[cd->loc] = inst;
	cd->code[cd->loc + 1] = sym;
	cd->line[cd->loc] = cd->line[cd->loc + 1] = (kushort)line;
	cd->loc += 2;
}

// each용
void kh_cd_emit_each(khCodeData* cd, kuint line, kuint inst, kint ko, kint vo)
{
	kh_cd_expand(cd, 3);
	cd->code[cd->loc] = inst;
	cd->code[cd->loc + 1] = *(kuint*)&ko;
	cd->code[cd->loc + 2] = *(kuint*)&vo;
	cd->line[cd->loc] = cd->line[cd->loc + 1] = cd->line[cd->loc + 2] = (kushort)line;
	cd->loc += 3;
}

// 정수값(+실수값)
void kh_cd_emit_32(khCodeData* cd, kuint line, kuint inst, kint value)
{
	kh_cd_expand(cd, 2);
	cd->code[cd->loc] = inst;
	cd->code[cd->loc + 1] = *(kuint*)&value;
	cd->line[cd->loc] = cd->line[cd->loc + 1] = (kushort)line;
	cd->loc += 2;
}

// 롱값(+더블값)
void kh_cd_emit_64(khCodeData* cd, kuint line, kuint inst, klong value)
{
	kh_cd_expand(cd, 3);
	cd->code[cd->loc] = inst;
	*(klong*)&cd->code[cd->loc + 1] = value;
	cd->line[cd->loc] = cd->line[cd->loc + 1] = cd->line[cd->loc + 2] = (kushort)line;
	cd->loc += 3;
}

// 줄설정
void kh_cd_set_line(khCodeData* cd, kint n, kuint line)
{
	if (n < cd->loc)
		cd->line[n] = (kushort)line;
}

// 위치
kint kh_cd_get_loc(khCodeData* cd)
{
	return cd->loc;
}

// 위치 바꿈
kint kh_cd_swap_loc(khCodeData* cd, kint v)
{
	kint n = cd->loc;
	cd->loc = v;
	return n;
}

// 위치 설정
void kh_cd_set_loc(khCodeData* cd, kint v)
{
	cd->loc = v;
}

// 코드 데이터
kuint* kh_cd_get_code(khCodeData* cd)
{
	return cd->code;
}

// 라인 데이터
kushort* kh_cd_get_line(khCodeData* cd)
{
	return cd->line;
}

// 코드 어디
kuint kh_cd_get_code_nth(khCodeData* cd, kint n)
{
	return cd->code[n];
}

// 코드 뒤로 어디
kuint kh_cd_get_code_inv(khCodeData* cd, kint n)
{
	return cd->code[cd->loc - n - 1];
}

// 라인 어디
kushort kh_cd_get_line_nth(khCodeData* cd, kint n)
{
	return cd->line[n];
}

// 라인 뒤로 어디
kushort kh_cd_get_line_inv(khCodeData* cd, kint n)
{
	return cd->line[cd->loc - n - 1];
}


//////////////////////////////////////////////////////////////////////////
// 함수 데이터

// 만들기
khFuncData* kh_fd_new(void)
{
	khFuncData* fd = k_new_0(khFuncData);

	kh_cd_init(&fd->code);

	k_arr_init(kUintArr, &fd->syms, 0);
	k_parr_init(&fd->reds, 0);

	k_arr_init(khBranchArr, &fd->brcs, 0);
	k_arr_init(khVarArr, &fd->vars, 0);
	k_arr_init(khLabelArr, &fd->lbls, 0);
	k_arr_init(khLabelArr, &fd->rsvs, 0);

	fd->line = 1;

	return fd;
}

// 제거
void kh_fd_delete(khFuncData* fd)
{
	kh_cd_disp(&fd->code);

	k_arr_disp(kUintArr, &fd->syms);
	k_parr_disp(&fd->reds);

	k_arr_disp(khBranchArr, &fd->brcs);
	k_arr_disp(khVarArr, &fd->vars);
	k_arr_disp(khLabelArr, &fd->lbls);
	k_arr_disp(khLabelArr, &fd->rsvs);

	k_delete(fd);
}

// 리셋
void kh_fd_reset(khFuncData* fd, kcham isfailed)
{
	if (isfailed)
		k_parr_loopeach(&fd->reds, k_base_unload);

	kh_cd_reset(&fd->code);

	k_arr_clear(kUintArr, &fd->syms);
	k_parr_clear(&fd->reds);

	k_arr_clear(khBranchArr, &fd->brcs);
	k_arr_clear(khVarArr, &fd->vars);
	k_arr_clear(khLabelArr, &fd->lbls);
	k_arr_clear(khLabelArr, &fd->rsvs);

	fd->args = fd->lcls = fd->tmps = fd->index = 0;
	fd->line = 1;
	fd->filename = fd->name = NULL;
}

// 상수 클리어
void kh_fd_reset_consts(khFuncData* fd)
{
	k_parr_loopeach(&fd->reds, k_base_unload);
}

// 심볼
kuint kh_fd_const_sym(khFuncData* fd, const char* str)
{
	kuint sym = k_symflx(str);
	kint ret;
	k_arr_contains(kUintArr, &fd->syms, sym, &ret);
	if (ret < 0)
		k_arr_add(kUintArr, &fd->syms, sym);
	return sym;
}

// 문자열
kint kh_fd_const_str(khFuncData* fd, const char* str)
{
	kRedStr* rs;
	kssize_t i;

	for (i = 0; i < k_parr_count(&fd->reds); i++)
	{
		rs = (kRedStr*)k_parr_nth(&fd->reds, i);

		if (k_base_get_type((kBase*)rs) != KTYPE_STR)
			continue;

		if (!k_streqv(str, rs->data))
			continue;

		return (kint)i;
	}

	rs = k_redstr_new(str, -1);
	k_parr_add(&fd->reds, rs);

	return (kint)i;	// i애 최종값이 들어있으므로 바로 반환
}

// 유니코드 문자열
kint kh_fd_const_wcs(khFuncData* fd, const kwchar* str)
{
	kRedWcs* rw;
	kssize_t i;

	for (i = 0; i < k_parr_count(&fd->reds); i++)
	{
		rw = (kRedWcs*)k_parr_nth(&fd->reds, i);

		if (k_base_get_type((kBase*)rw) != KTYPE_WCS)
			continue;

		if (!k_wcseqv(str, rw->data))
			continue;

		return (kint)i;
	}

	rw = k_redwcs_new(str, -1);
	k_parr_add(&fd->reds, rw);

	return (kint)i;	// i애 최종값이 들어있으므로 바로 반환
}

// 함수
kint kh_fd_const_fn(khFuncData* fd, kRedFn* fn)
{
	kint n = (kint)k_parr_count(&fd->reds);
	k_parr_add(&fd->reds, fn);
	return n;
}

// 변수 마크
kcham kh_fd_var_mark(khFuncData* fd, kint offset)
{
	kssize_t i;

	k_return_value_if_fail(offset >= KHSCP_LOCAL, TRUE);

	for (i = 0; i < k_arr_count(&fd->vars); i++)
	{
		struct khVarData* vd = &k_arr_nth(&fd->vars, i);

		if (vd->off == offset)
		{
			if (vd->asgn)
				return TRUE;
			else
			{
				vd->asgn = TRUE;
				return FALSE;
			}
		}
	}

	return TRUE;
}

// 변수 셋
kint kh_fd_var_set(khFuncData* fd, enum khScope scope, khToken* token, kcham append, kcham impl)
{
	struct khVarData vd, *pvd;
	kssize_t i;

	for (i = 0; i < k_arr_count(&fd->vars); i++)
	{
		pvd = &k_arr_inv(&fd->vars, i);

		if (!k_streqv(token->value.str, pvd->token->value.str))
			continue;

		if (scope == KHSCP_LOCAL && pvd->off >= 0)
		{
			// 로컬 발견
			return pvd->off;
		}

		if (pvd->off == scope)
		{
			// 로컬 이외 발견
			// 이 값은 인덱스가 아니고 스코프 값 중 하나.
			return pvd->off;
		}
	}

	// 새 변수
	if (scope == KHSCP_LOCAL)
	{
		vd.sym = 0;
		vd.off = fd->lcls++;
		vd.asgn = FALSE;
	}
	else
	{
		vd.sym = kh_fd_const_sym(fd, token->value.str);
		vd.off = scope;
		vd.asgn = TRUE;
	}

	vd.impl = impl;
	vd.token = token;

	if (append)
		k_arr_add(khVarArr, &fd->vars, vd);
	else
		k_arr_insert(khVarArr, &fd->vars, 0, vd);

	return vd.off;
}

// 변수 겟
kint kh_fd_var_get(khFuncData* fd, enum khScope scope, khToken* token, struct khVarData** vd)
{
	struct khVarData* pvd;
	kssize_t i;

	if (scope == KHSCP_UNKNOWN)
	{
		for (i = 0; i < k_arr_count(&fd->vars); i++)
		{
			pvd = &k_arr_inv(&fd->vars, i);

			if (k_streqv(token->value.str, pvd->token->value.str))
			{
				if (vd)
					*vd = pvd;

				return pvd->off;
			}
		}
	}
	else if (scope == KHSCP_LOCAL)
	{
		for (i = 0; i < k_arr_count(&fd->vars); i++)
		{
			pvd = &k_arr_inv(&fd->vars, i);

			if (pvd->off >= 0 && k_streqv(token->value.str, pvd->token->value.str))
			{
				if (vd)
					*vd = pvd;

				return pvd->off;
			}
		}
	}
	else
	{
		for (i = 0; i < k_arr_count(&fd->vars); i++)
		{
			pvd = &k_arr_inv(&fd->vars, i);

			if (pvd->off == scope && k_streqv(token->value.str, pvd->token->value.str))
			{
				if (vd)
					*vd = pvd;

				return pvd->off;
			}
		}
	}

	if (vd)
		*vd = NULL;

	return KHSCP_UNKNOWN;
}

// 브랜치 추가
void kh_fd_branch_add(khFuncData* fd, kuint at, kcham isif)
{
	struct khBranchData br;
	br.addr = at;
	br.isif = isif;
	k_arr_add(khBranchArr, &fd->brcs, br);
}

// 라벨 추가
kint kh_fd_label_set(khFuncData* fd, kuint addr, khToken* token, khToken** rettoken)
{
	struct khLabelData ld, *pld;
	kssize_t i;

	k_return_value_if_fail(token != NULL, addr);

	for (i = 0; i < k_arr_count(&fd->lbls); i++)
	{
		pld = &k_arr_nth(&fd->lbls, i);

		if (k_streqv(token->value.str, pld->token->value.str))
		{
			if (rettoken)
				*rettoken = pld->token;

			return pld->addr;
		}
	}

	ld.ref = 0;
	ld.addr = addr;
	ld.token = token;
	k_arr_add(khLabelArr, &fd->lbls, ld);

	return addr;
}

// 라벨 얻기
kint kh_fd_label_get(khFuncData* fd, khToken* token, khToken** rettoken)
{
	struct khLabelData* pld;
	kssize_t i;

	k_return_value_if_fail(token != NULL, -1);

	for (i = 0; i < k_arr_count(&fd->lbls); i++)
	{
		pld = &k_arr_nth(&fd->lbls, i);

		if (k_streqv(token->value.str, pld->token->value.str))
		{
			if (rettoken)
				*rettoken = pld->token;

			pld->ref++;

			return pld->addr;
		}
	}

	return -1;
}

// 라벨 예약
void kh_fd_label_reserve(khFuncData* fd, kuint addr, khToken* token)
{
	struct khLabelData ld;

	k_return_if_fail(token != NULL);

	ld.ref = 0;
	ld.addr = addr;
	ld.token = token;
	k_arr_add(khLabelArr, &fd->rsvs, ld);
}


//////////////////////////////////////////////////////////////////////////
// 디셈블

K_CTNR_DECL(khObjCtn, kBase*);

struct khDasm
{
	kFile* file;
	kRedFn* rfn;
};

static void _kh_dasm_file_write(kFile* fp, const kBstr1k* bs)
{
	k_file_write(fp, bs->data, 0, bs->len);
}

const char* _kh_dasm_fill_num(kint n1, kint n2, kint width, kBstr1k* bs)
{
	k_bstr_format(bs, 1024, "%d, %d", n1, n2);

	if (bs->len < width)
		k_bstr_set_rep(bs, ' ', width - bs->len);

	return bs->data;
}

static const char* _kh_dasm_find_sym(kuint sym, kBstr1k* bs)
{
	const char* psz;
	ksize_t len;

	if (!k_symunvalue(sym, &psz, &len))
		return "<unknown>";
	else
	{
		k_bstr_set_len(bs, psz, (kint)len);
		k_bstr_rem_chars(bs, "\n\r\t\v\a\b\f");
		return bs->data;
	}
}

static const char* _kh_dasm_find_fn(kRedFn* rfn, ksize_t n, kBstr1k* bs)
{
	if (n >= k_ctnr_count(rfn->objs))
		return "<unknown>";
	else
	{
		kRedFn* fn = (kRedFn*)k_ctnr_nth(rfn->objs, n);

		if (k_base_get_type((kBase*)fn) != KTYPE_FN)
			return "<invalid>";
		else
		{
			k_bstr_set(bs, fn->name);
			return bs->data;
		}
	}
}

static const char* _kh_dasm_find_str(kRedFn* rfn, ksize_t n, kBstr1k* bs)
{
	if (n >= k_ctnr_count(rfn->objs))
		return "<unknown>";
	else
	{
		kRedStr* rs = (kRedStr*)k_ctnr_nth(rfn->objs, n);

		if (k_base_get_type((kBase*)rs) != KTYPE_STR)
			return "<invalid>";
		else
		{
			k_bstr_set_len(bs, rs->data, rs->len);
			k_bstr_rem_chars(bs, "\n\r\t\v\a\b\f");
			return bs->data;
		}
	}
}

static const char* _kh_dasm_find_wcs(kRedFn* rfn, ksize_t n, kBstr1k* bs)
{
	if (n >= k_ctnr_count(rfn->objs))
		return "<unknown>";
	else
	{
		kRedWcs* rw = (kRedWcs*)k_ctnr_nth(rfn->objs, n);

		if (k_base_get_type((kBase*)rw) != KTYPE_WCS)
			return "<invalid>";
		else
		{
			bs->len = (kint)k_conv_uni_to_utf8(bs->data, 1024 - 1, rw->data, rw->len);
			k_bstr_rem_chars(bs, "\n\r\t\v\a\b\f");
			return bs->data;
		}
	}
}

static const char* _kh_dasm_make_fn_name(kRedFn* rfn, kBstr1k* bs)
{
	k_bstr_set(bs, rfn->name ? rfn->name : "noname");

	for (rfn = rfn->parent; rfn; rfn = rfn->parent)
	{
		k_bstr_append(bs, " < ");
		k_bstr_append(bs, rfn->name ? rfn->name : "noname");
	}

	return bs->data;
}

static void _kh_dasm_dump_syms(kFile* file, kRedFn* rfn)
{
	kUintCtn* ctn = rfn->code->syms;
	ksize_t i, cnt = k_ctnr_count(ctn);

	for (i = 0; i < cnt; i++)
	{
		kBstr1k bs, mn;
		k_bstr_format(&bs, 1024, "       #%d. %s\n", i, _kh_dasm_find_sym(k_ctnr_nth(ctn, i), &mn));
		_kh_dasm_file_write(file, &bs);
	}
}

static void _kh_dasm_dump_reds(kFile* file, kRedFn* rfn)
{
	ksize_t i, cnt = k_ctnr_count(rfn->objs);

	for (i = 0; i < cnt; i++)
	{
		kBstr1k bs, mn;
		kBase* base = k_ctnr_nth(rfn->objs, i);

		switch (k_base_get_type(base))
		{
			case KTYPE_FN:
				k_bstr_format(&bs, 1024, "       $%d. [F], %s\n", i, _kh_dasm_find_fn(rfn, i, &mn));
				break;

			case KTYPE_STR:
				k_bstr_format(&bs, 1024, "       $%d. [S], \"%s\"\n", i, _kh_dasm_find_str(rfn, i, &mn));
				break;

			case KTYPE_WCS:
				k_bstr_format(&bs, 1024, "       $%d. [W], \"%s\"\n", i, _kh_dasm_find_wcs(rfn, i, &mn));
				break;

			default:
				k_bstr_format(&bs, 1024, "       $%d. [?], %s\n", "<unknown>");
				break;
		}

		_kh_dasm_file_write(file, &bs);
	}
}

static void _kh_dasm_dump_code(kFile* file, kRedFn* rfn)
{
	khFnCode* redcode = rfn->code;
	kint size = redcode->data.size;
	kuint* codes = redcode->data.code;
	kushort* lines = redcode->data.line;

	kint ccd = 0;
	kint cln = lines[0];

	kBstr1k bs, mn;
	const char* psz;
	kany64 a1, a2, a3;

	while (ccd < size)
	{
		if (cln == lines[ccd])
			k_bstr_zero(&bs);
		else
		{
			cln = lines[ccd];
			k_bstr_set_char(&bs, '\n');
		}

		k_bstr_append_format(&bs, 1024, "%5d:%04X ", lines[ccd], ccd);
		k_bstr_append_format(&bs, 1024, "%-16s", kh_tos_mnemonic(codes[ccd]));

		switch (codes[ccd])
		{
			case KHMNE_STFLD:
			case KHMNE_LDFLD:
			case KHMNE_STPUB:
			case KHMNE_STMEM:
			case KHMNE_LDPUB:
			case KHMNE_LDMEM:
			case KHMNE_LDSYM:
			case KHMNE_BREAKPOINT:
				psz = _kh_dasm_find_sym(codes[++ccd], &mn);
				k_bstr_append_bls(&bs, &mn);
				break;

			case KHMNE_STLOC:
			case KHMNE_LDLOC:
				k_bstr_append_format(&bs, 1024, "%d", *(kint*)&codes[++ccd]);
				break;

			case KHMNE_BR:
			case KHMNE_BRZ:
			case KHMNE_BRNZ:
			case KHMNE_BRE:
			case KHMNE_BRNE:
			case KHMNE_BRSELECT:
			case KHMNE_T_IFELSE:
			case KHMNE_T_AND:
			case KHMNE_T_OR:
				k_bstr_append_format(&bs, 1024, "%04Xh", codes[++ccd]);
				break;

			case KHMNE_LDNULL:
				k_bstr_append(&bs, "null");
				break;

			case KHMNE_LDFALSE:
				k_bstr_append(&bs, "FALSE");
				break;

			case KHMNE_LDTRUE:
				k_bstr_append(&bs, "true");
				break;

			case KHMNE_LDC_I_0:
			case KHMNE_LDC_I_1:
			case KHMNE_LDC_I_2:
			case KHMNE_LDC_I_3:
			case KHMNE_LDC_I_4:
			case KHMNE_LDC_I_5:
			case KHMNE_LDC_I_6:
			case KHMNE_LDC_I_7:
			case KHMNE_LDC_I_8:
				k_bstr_append_format(&bs, 1024, "%d", codes[ccd] - KHMNE_LDC_I_0);
				break;

			case KHMNE_LDC_I4:
				a1.i32 = *(kint*)&codes[++ccd];
				if (a1.i32 < 10)
					k_bstr_append_format(&bs, 1024, "%d", a1.i32);
				else
					k_bstr_append_format(&bs, 1024, "%-16d ; %08Xh", a1.i32, a1.i32);
				break;

			case KHMNE_LDC_I8:
				a1.i64 = *(klong*)&codes[++ccd];
				if (a1.i64 < 10)
					k_bstr_append_format(&bs, 1024, "%lld", a1.i64);
				else
					k_bstr_append_format(&bs, 1024, "%-16lld ; %016llXh", a1.i64, a1.i64);
				ccd++;
				break;

			case KHMNE_LDC_R4:
				k_bstr_append_format(&bs, 1024, "%f", *(float*)&codes[++ccd]);
				break;

			case KHMNE_LDC_R8:
				k_bstr_append_format(&bs, 1024, "%lf", *(double*)&codes[++ccd]);
				ccd++;
				break;

			case KHMNE_LDSTR:
				a1.u = codes[++ccd];
				psz = _kh_dasm_find_str(rfn, a1.u, &mn);
				k_bstr_append_format(&bs, 1024, "$%-15u ; \"%s\"", a1.u, psz);
				break;

			case KHMNE_LDWCS:
				a1.u = codes[++ccd];
				psz = _kh_dasm_find_wcs(rfn, a1.u, &mn);
				k_bstr_append_format(&bs, 1024, "$%-15u ; \"%s\"", a1.u, psz);
				break;

			case KHMNE_LDFN:
				a1.u = codes[++ccd];
				psz = _kh_dasm_find_fn(rfn, a1.u, &mn);
				k_bstr_append_format(&bs, 1024, "$%-15u ; '%s'", a1.u, psz);
				break;

			case KHMNE_CALL:
				k_bstr_append_format(&bs, 1024, "%d", *(kint*)&codes[++ccd]);
				break;

			case KHMNE_PREPEACH:
				a1.u = codes[++ccd];
				a2.i = *(kint*)&codes[++ccd];
				if (a2.i < 0)
					k_bstr_append_format(&bs, 1024, "%-16u ; start index=%u", a1.u, a1.u);
				else
				{
					psz = _kh_dasm_fill_num((kint)a1.i, a2.i, 16, &mn);
					k_bstr_append_format(&bs, 1024, "%s ; start index=%u, key=%d", psz, a1.u, a2.i);
				}
				break;

			case KHMNE_DISPEACH:
				k_bstr_append(&bs, "");
				break;

			case KHMNE_LOOPEACH_DBL:
				a1.i = *(kint*)&codes[++ccd];
				a2.i = *(kint*)&codes[++ccd];
				psz = _kh_dasm_fill_num(a1.i, a2.i, 16, &mn);
				k_bstr_append_format(&bs, 1024, "%s ; key=%d, value=%d", psz, a1.i, a2.i);
				break;

			case KHMNE_LOOPEACH_SGL:
				a2.i = *(kint*)&codes[++ccd];
				k_bstr_append_format(&bs, 1024, "%-16d ; value=%d", a2.i, a2.i);
				break;

			case KHMNE_SELECT:
				a1.i = *(kint*)&codes[ccd - 2];
				a2.i = *(kint*)&codes[++ccd];
				a3.i = *(kint*)&codes[++ccd];
				k_bstr_append_format(&bs, 1024, "%d, %04Xh, %04Xh", a2.i, a3.i, codes[++ccd]);
				if (a2.i > 0)
				{
					kint i, z;

					k_bstr_append_char(&bs, '\n');
					_kh_dasm_file_write(file, &bs);
					ccd++;

					for (i = 0; i < a2.i * 2; i += 2)
					{
						k_bstr_set(&bs, "           ");

						switch (a1.i)
						{
							case KHMNE_LDSTR:
								a3.i = codes[ccd + i];
								psz = _kh_dasm_find_str(rfn, a3.i, &mn);
								k_bstr_append_format(&bs, 1024, "                %04Xh, $%-8d ; \"%s\"\n", codes[ccd + i + 1], a3.i, psz);
								break;

							case KHMNE_LDC_R4:
								k_bstr_append_format(&bs, 1024, "                %04Xh, %f\n", codes[ccd + i + 1], *(float*)&codes[ccd + i]);
								break;

							case KHMNE_LDC_I4:
								z = *(kint*)&codes[ccd + i];
								k_bstr_append_format(&bs, 1024, "                %04Xh, %-9d ; %08Xh\n", codes[ccd + i + 1], z, z);
								break;

							default:
								break;
						}

						_kh_dasm_file_write(file, &bs);
					}

					ccd += i;
					continue;
				}
				break;

			case KHMNE_PREPSELECT:
				a1.i = *(kint*)&codes[++ccd];
				a2.i = *(kint*)&codes[++ccd];
				k_bstr_append_format(&bs, 1024, "%d, %d", a1.i, a2.i);
				break;

			case KHMNE_NEWOBJ:
				a1.i = *(kint*)&codes[++ccd];
				a2.u = codes[++ccd];
				psz = _kh_dasm_find_sym(a2.u, &mn);
				k_bstr_append_format(&bs, 1024, "%s, %d", psz, a1.i);    // 원래 코드는 val, sym 순서임
				break;
		}

		k_bstr_append_char(&bs, '\n');
		_kh_dasm_file_write(file, &bs);

		ccd++;
	}
}

static void _kh_dasm_dump_fns(kFile* file, kRedFn* rfn)
{
	khFnCode* code = rfn->code;
	kBstr1k bs, mn;
	ksize_t i;

	// 가지고 있는 함수
	if (rfn->objs)
	{
		for (i = 0; i < k_ctnr_count(rfn->objs); i++)
		{
			kRedFn* fn = (kRedFn*)k_ctnr_nth(rfn->objs, i);

			if (k_base_get_type((kBase*)fn) == KTYPE_FN)
				_kh_dasm_dump_fns(file, fn);
		}
	}

	// 인트로
	k_bstr_format(&bs, 1024, "function \"%s\"\n", _kh_dasm_make_fn_name(rfn, &mn));
	_kh_dasm_file_write(file, &bs);

	k_bstr_format(&bs, 1024, "    size: %d(%Xh), filename: \"%s\"\n",
		code->data.size, code->data.size, code->filename);
	_kh_dasm_file_write(file, &bs);

	// 변수
	k_bstr_format(&bs, 1024, "    vars: %d, args: %d, locals: %d\n",
		code->si.vars, code->si.args, code->si.lcls);
	_kh_dasm_file_write(file, &bs);

	// 심볼
	if (code->syms && k_ctnr_is_have(code->syms))
	{
		k_bstr_format(&bs, 1024, "    symbols: %d\n", k_ctnr_count(code->syms));
		_kh_dasm_file_write(file, &bs);
		_kh_dasm_dump_syms(file, rfn);
	}

	// 상수 테이블
	if (rfn->objs && k_ctnr_is_have(rfn->objs))
	{
		k_bstr_format(&bs, 1024, "    constants: %d\n", k_ctnr_count(rfn->objs));
		_kh_dasm_file_write(file, &bs);
		_kh_dasm_dump_reds(file, rfn);
	}

	// 한줄 띄고
	k_bstr_set_char(&bs, '\n');
	_kh_dasm_file_write(file, &bs);

	// 코드
	_kh_dasm_dump_code(file, rfn);

	// 한줄 띄고
	k_bstr_set_char(&bs, '\n');
	_kh_dasm_file_write(file, &bs);
}

//
kcham kh_dasm_write_file(kFile* file, kRedFn* rfn)
{
	k_return_value_if_fail(rfn, FALSE);
	k_return_value_if_fail(file, FALSE);

	k_base_load((kBase*)rfn);

	k_file_write(file, "\n", 0, 1);

	_kh_dasm_dump_fns(file, rfn);

	k_base_unload((kBase*)rfn);

	return TRUE;
}

//
kcham kh_dasm_write_output(const char* filename, kRedFn* rfn)
{
	kFile* file;
	kcham ret;

	k_return_value_if_fail(filename, FALSE);

	file = k_file_new(filename, "at");
	k_return_value_if_fail(file, FALSE);

	ret = kh_dasm_write_file(file, rfn);

	k_file_delete(file);

	return ret;
}

